/**
 * Software License, Version 1.0
 * 
 * Copyright 2008 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgment:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgment may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or Granules, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called Granules, 
 * nor may Indiana University or Community Grids Lab or Granules appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 * GENERATED USING SOFTWARE.
 */
package cgl.granules.exchange;

import java.io.IOException;

import cgl.granules.exception.DeploymentException;
import cgl.granules.exception.MarshallingException;
import cgl.granules.operation.Operation;
import cgl.granules.scheduler.ResourceMetrics;
import cgl.granules.scheduler.TaskLifecycleMetrics;
import cgl.granules.util.MarshallingUtil;

/**
 * This is a singleton class which encapsulates functionality related to
 * creation and reconstructing various exchanges. The
 * <code>ExchangeFactory</code> also incorporates functionality to access
 * various data elements within an exchange.
 * 
 * @author Shrideep Pallickara
 * 
 */
public class ExchangeFactory {

	private static final ExchangeFactory instance = new ExchangeFactory();
	private MarshallingUtil marshallingUtil = MarshallingUtil.getInstance();

	private ExchangeFactory() {
	}

	/**
	 * Retrieve the singleton instance for this factory.
	 * 
	 * @return The singleton instance for this factory.
	 */
	public static ExchangeFactory getInstance() {
		return instance;
	}

	/**
	 * Checks to see if a given exchange type is a valid one.
	 * 
	 * @param exchangeType
	 * @return <code>true</code> if the exchange is a valid one;
	 *         <code>false</code> otherwise.
	 */
	public boolean isValidExchangeType(int exchangeType) {
		boolean valid = false;
		if (exchangeType == ExchangeType.DEPLOYMENT
				|| exchangeType == ExchangeType.EXECUTION_STATUS
				|| exchangeType == ExchangeType.ADVERTISEMENT
				|| exchangeType == ExchangeType.DIAGNOSTIC
				|| exchangeType == ExchangeType.RESOURCE_DISCOVERY) {
			valid = true;
		}

		return valid;
	}

	/**
	 * Retrieves the exchange type information from a stream of bytes.
	 * 
	 * @param exchangeBytes
	 *            The marshalled representation of the exchange
	 * @return the exchangeType
	 * @throws MarshallingException
	 *             if there are problems with the exchange bytes
	 */
	public int getExchangeType(byte[] exchangeBytes) {
		/*
		 * if (exchangeBytes == null) { String errorReport =
		 * "Specified byte[] for reconstruction is NULL";
		 * throwMarshallingException(errorReport); }
		 */

		/* the first 4 bytes are reserved for the length of the base class */
		int exchangeType = marshallingUtil.getInt(exchangeBytes, 4);

		return exchangeType;
	}

	/**
	 * Creates a deployment request based on the specified parameters.
	 * 
	 * @param jobIdentifier
	 *            The ID to be associated with the deployment
	 * @param operation
	 *            The operation being deployed
	 * @param respondTo
	 *            the stream over which responses need to be sent.
	 * @return the deployment request duly initialized
	 * @throws DeploymentException
	 *             If problems are encountered with the specified parameters, or
	 *             if any of them are NULL.
	 */
	public DeploymentMessage createDepoymentRequest(String jobIdentifier,
			Operation operation, String respondTo) throws DeploymentException {
		DeploymentMessage deploymentMessage = new DeploymentMessage(
				jobIdentifier, operation, respondTo);
		return deploymentMessage;
	}

	/**
	 * Creates a DeploymentMessage that is a request for either getting the
	 * STATUS or ABORTing a previously deployment operation.
	 * 
	 * @param jobIdentifier
	 *            The ID to be associated with the deployment
	 * @param taskIdentifier
	 *            The task identifier that was assigned to one of the
	 *            constituent tasks that comprises the system-wide deployment.
	 * @param respondTo
	 *            The stream over which responses need to be sent.
	 * @param exchangeSubType
	 *            This should be either the STATUS or ABORT exchange subTypes.
	 * @throws DeploymentException
	 *             If problems are encountered with the specified parameters, or
	 *             if any of them are NULL or if the exchangeSubType is
	 *             something other than DeploymentMessage.STATUS or
	 *             DeploymentMessage.ABORT
	 */
	public DeploymentMessage createDeploymentStatusOrAbortRequest(
			String jobIdentifier, String taskIdentifier, String respondTo,
			int exchangeSubType) throws DeploymentException {

		DeploymentMessage deploymentMessage = new DeploymentMessage(
				jobIdentifier, taskIdentifier, respondTo, exchangeSubType);
		return deploymentMessage;

	}

	/**
	 * Creates a deployment response, to a previously issued request. This
	 * includes the task identifiers and the exchangeSUbType, which indicates
	 * whether this is a response to a previously issued DEPLOY, STATUS or ABORT
	 * request. A condition flag is also included in some cases to indicate the
	 * status of processing the previously issued request. The description
	 * element includes a detailed description of the response.
	 * 
	 * @param jobIdentifier
	 *            The ID to be associated with the deployment
	 * @param taskIdentifier
	 *            The task identifier that was assigned to one of the
	 *            constituent tasks that comprises the system-wide deployment.
	 * @param exchangeSubType
	 *            Indicates whether this is a response to a previously issued
	 *            DEPLOY, STATUS or ABORT
	 * @param conditionFlag
	 *            Indicates the status of processing the previously issued
	 *            request.
	 * @param description
	 *            A detailed description or reason for the response
	 * @throws DeploymentException
	 *             If there are problems creating this exchange.
	 */
	public DeploymentMessage createDeploymentResponse(String jobIdentifier,
			String taskIdentifier, int exchangeSubType, int conditionFlag,
			String description) throws DeploymentException {

		DeploymentMessage deploymentMessage = new DeploymentMessage(
				jobIdentifier, taskIdentifier, exchangeSubType, conditionFlag,
				description);

		return deploymentMessage;
	}

	/**
	 * Reconstructs the Deployment Message from a stream of bytes
	 * 
	 * @param exchangeBytes
	 *            The marshalled representation of the exchange
	 * @return The reconstructed <code>DeploymentMessage</code>
	 * @throws IOException
	 *             If there are problems reconstructing elements of the exchange
	 * @throws MarshallingException
	 *             If there are problems reconstructing specialized elements of
	 *             the exchange
	 */
	public DeploymentMessage getDeploymentMessage(byte[] exchangeBytes)
			throws IOException, MarshallingException {

		/* the first 4 bytes are reserved for the length of the base class */
		int exchangeType = marshallingUtil.getInt(exchangeBytes, 4);
		if (exchangeType != ExchangeType.DEPLOYMENT) {
			String errorReport = "The field ExchangeType=" + exchangeType
					+ " implies that this is not a Deployment Message";
			throwMarshallingException(errorReport);
		}

		DeploymentMessage deployment = new DeploymentMessage();
		deployment.reconstruct(exchangeBytes);
		return deployment;
	}

	/**
	 * Creates an <code>ExecutionStatus</code> REQUEST based on the specified
	 * parameters
	 * 
	 * @param jobIdentifier
	 *            The job identifier that the task is a part of
	 * @param taskIdentifier
	 *            The identifier of the task in question
	 * @param respondTo
	 *            The stream to which the responses should be sent
	 * @return The created execution status exchange.
	 */
	public ExecutionStatus createExecutionStatusRequest(String jobIdentifier,
			String taskIdentifier, String respondTo) {
		ExecutionStatus executionStatus = new ExecutionStatus(jobIdentifier,
				taskIdentifier, respondTo);
		return executionStatus;
	}

	/**
	 * Creates an <code>ExecutionStatus</code> RESPONSE based on the specified
	 * parameters
	 * 
	 * @param jobIdentifier
	 *            The job identifier that the task is a part of
	 * @param taskIdentifier
	 *            The identifier of the task in question
	 * @param conditionFlag
	 *            The status of the execution (based on constants in the
	 *            <code>ConditionFlag</code>)
	 * @param description
	 *            A description of the response
	 * @param taskLifecycleMetrics
	 *            The metrics associated with the execution.
	 * @param respondTo
	 *            The stream to which the responses should be sent
	 * @return The created execution status exchange.
	 */
	public ExecutionStatus createExecutionStatusResponse(String jobIdentifier,
			String taskIdentifier, int conditionFlag, String description,
			TaskLifecycleMetrics taskLifecycleMetrics, String respondTo) {
		ExecutionStatus executionStatus = new ExecutionStatus(jobIdentifier,
				taskIdentifier, conditionFlag, description,
				taskLifecycleMetrics, respondTo);
		return executionStatus;
	}

	/**
	 * Reconstruct the execution status object from a byte[].
	 * 
	 * @param exchangeBytes
	 *            The byte[] representation of the execution status.
	 * @return The reconstructed execution status object.
	 * @throws IOException
	 *             If there are problems reconstructing elements of the exchange
	 * @throws MarshallingException
	 *             If there are problems reconstructing specialized elements of
	 *             the exchange
	 */
	public ExecutionStatus getExecutionStatus(byte[] exchangeBytes)
			throws IOException, MarshallingException {
		/* the first 4 bytes are reserved for the length of the base class */
		int exchangeType = marshallingUtil.getInt(exchangeBytes, 4);
		if (exchangeType != ExchangeType.EXECUTION_STATUS) {
			String errorReport = "The field ExchangeType=" + exchangeType
					+ " implies that this is not a ExecutionStatus exchange";
			throwMarshallingException(errorReport);
		}
		ExecutionStatus executionStatus = new ExecutionStatus();
		executionStatus.reconstruct(exchangeBytes);
		return executionStatus;
	}

	/**
	 * Create a ResourceDiscovery request
	 * 
	 * @param resourceType
	 *            The type of the resource the node is looking for
	 * @param respondTo
	 *            The stream over which responses need to be sent.
	 * 
	 * @return The created ResourceDiscovery request
	 */
	public ResourceDiscovery createResourceDiscoveryRequest(int resourceType,
			String respondTo) {
		ResourceDiscovery resourceDiscovery = new ResourceDiscovery(
				resourceType, respondTo);
		return resourceDiscovery;
	}

	/**
	 * Creates a <code>ResourceDiscovery</code> response
	 * 
	 * @param resourceMetrics
	 *            The metrics associated with the resource.
	 * @param respondTo
	 *            The stream over which the resource listens to deployment
	 *            requests
	 * @return The created <code>ResourceDiscovery</code> response.
	 */
	public ResourceDiscovery createResourceDiscoveryResponse(int conditionFlag,
			String description, ResourceMetrics resourceMetrics,
			String respondTo) {
		ResourceDiscovery resourceDiscovery = new ResourceDiscovery(
				conditionFlag, description, resourceMetrics, respondTo);
		return resourceDiscovery;
	}

	/**
	 * Reconstructs the <code>ResourceDiscovery</code> from a stream of bytes
	 * 
	 * @param exchangeBytes
	 *            The marshalled representation of the exchange
	 * @return The reconstructed <code>ResourceDiscovery</code>
	 * @throws IOException
	 *             If there are problems reconstructing elements of the exchange
	 * @throws MarshallingException
	 *             If there are problems reconstructing specialized elements of
	 *             the exchange
	 */
	public ResourceDiscovery getResourceDiscovery(byte[] exchangeBytes)
			throws IOException, MarshallingException {
		/* the first 4 bytes are reserved for the length of the base class */
		int exchangeType = marshallingUtil.getInt(exchangeBytes, 4);
		if (exchangeType != ExchangeType.RESOURCE_DISCOVERY) {
			String errorReport = "The field ExchangeType=" + exchangeType
					+ " implies that this is not a ResourceDiscovery message";
			throwMarshallingException(errorReport);
		}

		ResourceDiscovery resourceDiscovery = new ResourceDiscovery();
		resourceDiscovery.reconstruct(exchangeBytes);
		return resourceDiscovery;
	}

	/**
	 * Throws a MarshallingException with the specified reason
	 * 
	 * @param errorReport
	 * @throws MarshallingException
	 */
	private void throwMarshallingException(String errorReport)
			throws MarshallingException {
		throw new MarshallingException(errorReport);
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
